use crate::cfg;

use dot_generator::*;
use dot_structures::*;
use graphviz_rust::printer::{DotPrinter, PrinterContext};

// 返回 bool = true 意味着至少有一个 AddressedInst.interested = true.
fn insts2label(cfgnode: &cfg::Node, cfggraph: &cfg::Graph) -> (String, bool) {
    if cfgnode.insts.is_empty() {
        return ("\"迷茫! 特别迷茫!\"".to_string(), false);
    }
    let mut interested = false;
    let mut labelline = vec![
        "<".to_string(),
        "<TABLE BORDER=\"0\" CELLBORDER=\"0\">".to_string(),
    ];
    for &inst_id in &cfgnode.insts {
        let inst = &cfggraph.insts[inst_id];
        interested = interested || inst.interested;
        let font_begin = if inst.interested {
            "<FONT COLOR=\"red\">"
        } else {
            ""
        };
        let font_end = if inst.interested { "</FONT>" } else { "" };
        let linestr = format!(
            "<TR><TD ALIGN=\"RIGHT\">{}{:#x}{}</TD><TD ALIGN=\"LEFT\">{}</TD></TR>",
            font_begin,
            inst.addr,
            font_end,
            inst.inst.to_string()
        );
        labelline.push(linestr);
    }
    labelline.push("</TABLE>".to_string());
    labelline.push(">".to_string());
    return (labelline.join(" "), interested);
}

fn add_node(dotgraph: &mut Graph, cfgnode: &cfg::Node, cfggraph: &cfg::Graph) {
    let (node_label, interested) = insts2label(cfgnode, cfggraph);
    let mut retnode = node!(
        cfgnode.id ;
        attr!("label", node_label),
        attr!("shape", "box")
    );
    if interested {
        retnode.attributes.push(attr!("style", "filled"));
        retnode.attributes.push(attr!("fillcolor", "yellow"));
    }
    dotgraph.add_stmt(Stmt::Node(retnode));
    return;
}

fn add_edge(dotgraph: &mut Graph, cfgedge: &cfg::Edge) {
    let (color, style, label) = match &cfgedge.kind {
        cfg::EdgeKind::Jmp => ("black", "bold", "".to_string()),
        cfg::EdgeKind::Jcc(desc) => ("blue", "dashed", desc.clone()),
        cfg::EdgeKind::Serial(desc) => ("green", "dashed", desc.clone()),
    };
    let label = format!("\"{}\"", label);
    let dotedge = edge!(
        node_id!(cfgedge.from) => node_id!(cfgedge.to);
        attr!("color", color),
        attr!("style", style),
        attr!("label", label)
    );
    dotgraph.add_stmt(Stmt::Edge(dotedge));
    return;
}

pub fn dot_render(graph: &cfg::Graph) -> String {
    let mut dotgraph = graphviz_rust::parse(
        r#"
        strict digraph G {
            label="as2cfg - Generated By hidva/as2cfg";
        }
    "#,
    )
    .unwrap();

    for node in &graph.nodes {
        add_node(&mut dotgraph, node, graph);
    }
    for edge in &graph.edges {
        add_edge(&mut dotgraph, edge);
    }

    let mut ctx = PrinterContext::default();
    ctx.with_indent_step(4);
    return dotgraph.print(&mut ctx);
}
